<?php
/**
 * Created by IntelliJ IDEA.
 * User: ty42
 * Date: 23/07/2016
 * Time: 12:48 PM
 */



class Tradem extends CI_Model
{
    //
    const BUSINESS_ZIPCODE_EXPORT_POST = '11';      // 专家咨询帖
    const BUSINESS_ZIPCODE_USER_WALLET = '22';      // 钱包交易
    const BUSINESS_ZIPCODE_MEDICAL_PICS = '32';     // 插画

    static $business_zipcode = array(
        self::BUSINESS_ZIPCODE_EXPORT_POST => '远程咨询帖',
        self::BUSINESS_ZIPCODE_USER_WALLET => '钱包交易',
        self::BUSINESS_ZIPCODE_MEDICAL_PICS => '插画',
    );

    const NOTIFY_URL_USER_WALLET = PREFIX_API .'pay/alipay/notify_trade';


    // cache
    const TIME_OUT_PAY = 180;
    const CACHE_PRE_TRADE = 'trade_';
    const CACHE_PRE_BASIC_DATA = 'trade_basic_data_';
    const CACHE_RP_RECEIVER = 'cache_rp_receiver_';
    const CACHE_RED_PACKET = 5 * 60;
    const TIME_OUT_RED_PACKET = 48 * 60 * 60;

    // db
    const TBL_USER_ACCOUNT = 'qb_user_wallet';
    const TBL_USER_ACCOUNT_HISTORY = 'qb_user_wallet_history';
    const TBL_TRADE = 'qb_user_trade';
    const TBL_TRADE_DETAIL = 'qb_user_trade_detail';
    const TBL_USER = 'ml_fellow';
    const TBL_BG_WITHDRAW_AUDIT = 'qb_op_withdraws_cash';
    const TBL_SMS_EXPAND_POST = 'ml_sms_expand_post';
    const TBL_USER_TRUST_RELATION = 'qb_user_trust_relation';

    const FIELD_AUDIT_ID = 'audit_id';

    // 支付方式, 1-100 系统预留, 101-00 其它
    const PAYMENT_WALLET = 1; // 钱包
    const PAYMENT_PAL_PAYMENT = 2; // 好友支付
    const PAYMENT_UNFOLD_RED_PACKET = 3; // 拆红包
    const PAYMENT_REFUND_WALLET = 31; // 退款至钱包
    const PAYMENT_ALIPAY = 101; // 支付宝
    const PAYMENT_JFPOINTS = 201; // 定义给积分支付



    // 凭据类别, 1-100 系统预留, 101-00 其它
    const RECEIPT_TYP_POST = 1; // 帖子
    const RECEIPT_TYP_COMMENT = 2; // 评论
    const RECEIPT_TYP_RED_PACKET = 3; // 红包
    const RECEIPT_TYP_PART = 5; // 图谱
    const RECEIPT_TYP_ALIPAY = 101; // 支付宝



    const TRADE_STATUS_INIT = 0;
    const TRADE_STATUS_READY = 1;
    const TRADE_STATUS_PAL_AUDIT = 18;      // 好友审核
    const TRADE_STATUS_PAL_AUDIT_FAILED = 19;      // 好友不同意
    const TRADE_STATUS_PAL_AUDIT_SUCCEED = 20;      // 好友同意
    const TRADE_STATUS_AUDIT = 21;   // 后台审核
    const TRADE_STATUS_AUDIT_FAILED = 22;   // 后台审核不过
    const TRADE_STATUS_AUDIT_SUCCEED = 24;   // 后台审核通过
    const TRADE_STATUS_EXPIRE = 31;
    const TRADE_STATUS_PRE_SUCCESS = 40;
    const TRADE_STATUS_SUCCESS = 41;


    static $trade_status_desc = array(
        self::TRADE_STATUS_INIT => "待校验",
        self::TRADE_STATUS_READY => "待支付",
        self::TRADE_STATUS_PAL_AUDIT => "申请已提交",
        self::TRADE_STATUS_PAL_AUDIT_FAILED => "申请被拒绝",
        self::TRADE_STATUS_PAL_AUDIT_SUCCEED => "申请已通过",
        self::TRADE_STATUS_AUDIT => "申请已提交",
        self::TRADE_STATUS_AUDIT_FAILED => "申请未通过",
        self::TRADE_STATUS_AUDIT_SUCCEED => "后台处理中",
        self::TRADE_STATUS_EXPIRE => "交易超时",
        self::TRADE_STATUS_PRE_SUCCESS => "支付完成",
        self::TRADE_STATUS_SUCCESS => "交易完成",
    );


    // 所有发生交易的渠道, 1-100 产品内消费, 101-10000 使用第三方进入产品消费, 10001-11000 第三方充值, 11001-12000 提现至第三方
    const TRADE_CHANNEL_UNDEFINED = 0;
    const TRADE_CHANNEL_AWARD_POST_WALLET = 1;
    const TRADE_CHANNEL_AWARD_COMMENT_WALLET = 2;
    const TRADE_CHANNEL_AWARD_RED_PACKET_WALLET = 3;
    const TRADE_CHANNEL_AWARD_RED_PACKET_RECEIVE = 4;
    const TRADE_CHANNEL_AWARD_PART_WALLET = 5;

    const TRADE_CHANNEL_REFUND_RED_PACKET_WALLET = 33;

    const TRADE_CHANNEL_AWARD_POST_PAL_WALLET = 51;
    const TRADE_CHANNEL_AWARD_COMMENT_PAL_WALLET = 52;

    const TRADE_CHANNEL_AWARD_LIMIT_BY_WALLET = 101;

    const TRADE_CHANNEL_AWARD_POST_ALIPAY = 101;
    const TRADE_CHANNEL_AWARD_COMMENT_ALIPAY = 102;
    const TRADE_CHANNEL_AWARD_RED_PACKET_ALIPAY = 103;
    const TRADE_CHANNEL_AWARD_PART_ALIPAY = 105;

    const TRADE_CHANNEL_AWARD_POST_WEICHAT = 201;

    const TRADE_CHANNEL_TOP_UP_ALIPAY = 10001;

    const TRADE_CHANNEL_WITH_DRAW_LIMIT_FROM_WALLET = 11001;

    const TRADE_CHANNEL_WITH_DRAW_ALIPAY = 11001;

    const TRADE_CHANNEL_WITH_DRAW_WEICHAT = 12000;


    static $trade_channel_desc = array(
        self::TRADE_CHANNEL_UNDEFINED => "未定义",
        self::TRADE_CHANNEL_AWARD_POST_WALLET => "打赏",
        self::TRADE_CHANNEL_AWARD_COMMENT_WALLET => "打赏",
        self::TRADE_CHANNEL_AWARD_RED_PACKET_WALLET => "发红包",
        self::TRADE_CHANNEL_AWARD_RED_PACKET_RECEIVE => "收红包",
        self::TRADE_CHANNEL_AWARD_PART_WALLET => "图谱购买",
        self::TRADE_CHANNEL_REFUND_RED_PACKET_WALLET => "红包过期退款",
        self::TRADE_CHANNEL_AWARD_POST_PAL_WALLET => "代付",
        self::TRADE_CHANNEL_AWARD_COMMENT_PAL_WALLET => "代付",
        self::TRADE_CHANNEL_AWARD_POST_ALIPAY => "打赏",
        self::TRADE_CHANNEL_AWARD_COMMENT_ALIPAY => "打赏",
        self::TRADE_CHANNEL_AWARD_RED_PACKET_ALIPAY => '发红包',
        self::TRADE_CHANNEL_AWARD_PART_ALIPAY => "图谱购买",
        self::TRADE_CHANNEL_AWARD_POST_WEICHAT => "打赏",
        self::TRADE_CHANNEL_TOP_UP_ALIPAY => "充值",
        self::TRADE_CHANNEL_WITH_DRAW_ALIPAY => "提现",
    );


    // 付款方式和凭据类别决定渠道
    static $trade_channel_relation = array(
        self::PAYMENT_WALLET => array(
            self::RECEIPT_TYP_POST => self::TRADE_CHANNEL_AWARD_POST_WALLET,
            self::RECEIPT_TYP_COMMENT => self::TRADE_CHANNEL_AWARD_COMMENT_WALLET,
            self::RECEIPT_TYP_RED_PACKET => self::TRADE_CHANNEL_AWARD_RED_PACKET_WALLET,
            self::RECEIPT_TYP_PART => self::TRADE_CHANNEL_AWARD_PART_WALLET,
            self::RECEIPT_TYP_ALIPAY => self::TRADE_CHANNEL_WITH_DRAW_ALIPAY,
        ),
        self::PAYMENT_PAL_PAYMENT => array(
            self::RECEIPT_TYP_POST => self::TRADE_CHANNEL_AWARD_POST_PAL_WALLET,
            self::RECEIPT_TYP_COMMENT => self::TRADE_CHANNEL_AWARD_COMMENT_PAL_WALLET,
        ),
        self::PAYMENT_UNFOLD_RED_PACKET => array(
            self::RECEIPT_TYP_RED_PACKET => self::TRADE_CHANNEL_AWARD_RED_PACKET_RECEIVE,
        ),
        self::PAYMENT_REFUND_WALLET => array(
            self::RECEIPT_TYP_RED_PACKET => self::TRADE_CHANNEL_REFUND_RED_PACKET_WALLET,
        ),
        self::PAYMENT_ALIPAY => array(
            self::RECEIPT_TYP_POST => self::TRADE_CHANNEL_AWARD_POST_ALIPAY,
            self::RECEIPT_TYP_COMMENT => self::TRADE_CHANNEL_AWARD_COMMENT_ALIPAY,
            self::RECEIPT_TYP_RED_PACKET => self::TRADE_CHANNEL_AWARD_RED_PACKET_ALIPAY,
            self::RECEIPT_TYP_PART => self::TRADE_CHANNEL_AWARD_PART_ALIPAY,
        )
    );


    const BROKER_TYPE_QY_USER = 0;              // 普通用户
    const BROKER_TYPE_QY_OPER = 1;              // 后台人员
    const BROKER_TYPE_QY_SYS = 2;               // 系统自动
    const BROKER_TYPE_THIRD_ALIPAY = 101;       // 第三方支付, 支付宝

    // 记录关键事务 sql error
    static $trans_errors = null;
    // 需要回滚
    private $need_rollback = true;


    const BALANCE_NOTIFY_LEVEL_ONE = 1000;      // 10 块钱提醒
    const BALANCE_NOTIFY_LEVEL_TWO = 5000;      // 50 块钱提醒

    public function __construct()
    {
        parent::__construct();
    }



    public function getMachineID()
    {
        $machine_ip = $_SERVER['SERVER_ADDR'];
        $tmp = explode('.', $machine_ip);
        return end($tmp);
    }

    public function getTradeNotifyUrl()
    {
        $this->load->helper('url');
        return site_url('pay/alipay/notify_trade');
    }

    /**
     * 生成交易 id
     * @param string $zipcode: 两位业务号
     * @return string
     */
    public function genTradeID($zipcode = '00')
    {
        $now = time();
        $mid = $this->getMachineID();
        $zipcode = str_pad($zipcode, 2, '0', STR_PAD_LEFT);
        $id = date('ymd', $now).$zipcode.substr($now,-5).str_pad($mid,3, '0', STR_PAD_LEFT).substr(microtime(),2,5);

        return $id;
    }


    public function getBusinessZipcodeByTradeID($trade_id)
    {
        $zipcode = substr($trade_id, 6, 2);
        if (isset(self::$business_zipcode[$zipcode])) {
            $zipcode = '00';
        }
        return $zipcode;
    }


    public function generateUserAccount($user_id)
    {
        if (! $user_id) {
            return array();
        }
        $now = time();
        $data = array(
            "uid" => $user_id,
//            "balance" => 0.0,
            "addtime" => $now,
            "utime" => $now,
        );

        // insert_id or false
        $res = $this->db->insert(self::TBL_USER_ACCOUNT, $data);

        $data["wid"] = $this->db->insert_id();
        $data["balance"] = 0;
        return $data;
    }


    public function genTradeErrorMSG($message, $error_code = -1)
    {
        return array("message" => $message, 'code' => $error_code);
    }


    public function getUserAccount($user_id)
    {
        $record = array();
        if ($user_id) {
            $query = $this->db->select('a.*, b.pay_passwd')
                ->from(self::TBL_USER_ACCOUNT . ' as a')
                ->join(self::TBL_USER . ' as b', 'a.uid=b.id', 'left')
                ->where('uid', $user_id)
                ->limit(1)
                ->get();
            if ($query) {
                $record = $query->row_array();
                $record['balance'] = isset($record['balance']) ? $record['balance'] : 0;
            }
        }

        return $record;
    }


    public function convertYuan2Fen($amount)
    {
        return intval(100*floatval($amount));
    }


    public function convertFen2Yuan($amount)
    {
        return number_format($amount / 100.0, 2, '.', '');
    }

    /**
     * @param CI_DB_mysqli_driver $db_instance
     */
    static function _fetchSQLError($db_instance)
    {
        if (! is_array($db_instance)) {
            self::$trans_errors = array();
        }

        $error = $db_instance->error();
        if (isset($error['code']) && $error['code']) {
            self::$trans_errors[] = $error;
        }
    }


    protected function _saveTrade2DB(&$trade_data)
    {
        if ($this->db->trans_status() === FALSE || $this->need_rollback) {
            return ;
        }
        $now = time();
        $new_trade = array(
            "trade_version" => '1.0.0',
            "addtime" => $now,
            "utime" => $now,
            "trade_status" => self::TRADE_STATUS_INIT,
        );

        $trade_data = array_merge($trade_data, $new_trade);

        $this->db->insert(self::TBL_TRADE, $trade_data);

        $trade_data['qbtid'] = $this->db->insert_id();

        self::_fetchSQLError($this->db);
    }


    protected function _updatePostRewardAmount($post_id, $amount)
    {
        if ($this->db->trans_status() === FALSE || $this->need_rollback) {
            return ;
        }
        $this->db->set('post_money', 'post_money+'.$amount, false);
        $this->db->where('id', $post_id);
        $this->db->update('ml_post');
        self::_fetchSQLError($this->db);
    }


    protected function _updateCommentRewardAmount($comment_id, $amount)
    {
        if ($this->db->trans_status() === FALSE || $this->need_rollback) {
            return ;
        }
        $this->db->set('comment_money', 'comment_money+'.$amount, false);
        $this->db->where('id', $comment_id);
        $this->db->update('ml_comment');
        self::_fetchSQLError($this->db);
    }


    protected function _updateRedPocketLeft($rpid, $amount)
    {
        if ($this->db->trans_status() === FALSE || $this->need_rollback) {
            return ;
        }
        $this->db->set('amount_left', 'amount_left+'.$amount, false);
        $this->db->set('count_left', 'count_left-1', false);
        $this->db->where('rpid', $rpid);
        $this->db->update(Walletm::TBL_RED_PACKET);
        self::_fetchSQLError($this->db);
    }


    protected function _setRedPocketBeRefunded($rpid)
    {
        if ($this->db->trans_status() === FALSE || $this->need_rollback) {
            return ;
        }
        $this->db->set('amount_left', 0);
        $this->db->set('count_left', 0);
        $this->db->set('refundtime', time());
        $this->db->where('rpid', $rpid);
        $this->db->where('count_left>', 0);
        $this->db->update(Walletm::TBL_RED_PACKET);
        self::_fetchSQLError($this->db);
    }


    protected function _saveBGWithdrawAudit($trade, &$update_data)
    {
        if ($this->db->trans_status() === FALSE || $this->need_rollback) {
            return ;
        }
        $data = array(
            "uid" => $trade['affect_user'],
            "money" => $trade['amount'],
            "tid" => $trade['trade_id'],
            "bind_account_zfb" => $trade['receipt'],
            "addtime" => time(),
        );
        $this->db->insert(self::TBL_BG_WITHDRAW_AUDIT, $data);

        $update_data[self::FIELD_AUDIT_ID] = $this->db->insert_id();
    }

    /**
     * @param array $user_account
     * @param int $amount
     * @param string $trade_id
     * @param int $channel
     * @return string|false
     */
    protected function _addUserAccountHistory(&$user_account, $amount, $trade_id, $channel)
    {
        if ($this->db->trans_status() === FALSE || $this->need_rollback) {
            return ;
        }
        $record_history = array(
            "wid" => $user_account["wid"],
            "uid" => $user_account["uid"],
            "amount" => $amount,
            "remaining" => $user_account["balance"] + $amount,
            "trade_id" => $trade_id,
            "cmmnt" => isset(self::$trade_channel_desc[$channel]) ? self::$trade_channel_desc[$channel] : '',
            "addtime" => time(),
        );
        // "生成用户钱包历史记录失败"
        $this->db->insert(self::TBL_USER_ACCOUNT_HISTORY, $record_history);
        self::_fetchSQLError($this->db);

        $user_account['balance'] = $record_history["remaining"];
    }


    /**
     * @param array $user_account 用户账户
     */
    protected function _updateUserBalance($user_account)
    {
        if ($this->db->trans_status() === FALSE || $this->need_rollback) {
            return ;
        }

        if ($user_account['balance'] < 0) {
            $this->need_rollback = true;
        }
        else {
            $data_upt = array('balance' => $user_account['balance'], 'utime' => time());

            $this->db->update(self::TBL_USER_ACCOUNT, $data_upt, array("wid"=>$user_account["wid"]));
            self::_fetchSQLError($this->db);
        }
    }


    protected function _updateTradeCache($trade_data)
    {
        if ($this->db->trans_status() === FALSE || $this->need_rollback) {
            return ;
        }
        if (isset($trade_data['trade_id'])) {
            ci_load_once('mc', 'cache');
            $this->mc->save(self::CACHE_PRE_TRADE . $trade_data['trade_id'], $trade_data, self::TIME_OUT_PAY);
        }
    }


    protected function _releaseRedPacket($rpid)
    {
        if ($this->db->trans_status() === FALSE || $this->need_rollback) {
            return ;
        }
        ci_load_once('walletm');
        $rpinfo = $this->walletm->getRedPacketInfoByID($rpid);
        $data_upt = array(
            'expiretime' => time() + self::TIME_OUT_RED_PACKET,
            'count_left' => $rpinfo['count_total'],
            'amount_left' => $rpinfo['amount_total'],
        );
        $this->db->update(Walletm::TBL_RED_PACKET, $data_upt, array("rpid" => $rpid));
        self::_fetchSQLError($this->db);
    }


    public function getTradeByTradeID($trade_id, $op_user)
    {
        if (empty($trade_id)) {
            return null;
        }

        $trade = $this->mc->get(self::CACHE_PRE_TRADE . $trade_id);
        if (empty($trade)) {
            $query = $this->db->where('trade_id', $trade_id)->get(self::TBL_TRADE, 1, 0);
            if ($query) {
                $trade = $query->row_array();
            }
        }

        $now = time();
        // 支付类有过期时间, 提现不需要自动过期
        if (! empty($op_user)                                                   // 调用接口的用户不能为空
            && $trade['channel'] >= self::TRADE_CHANNEL_AWARD_LIMIT_BY_WALLET   // 调用第三方支付类
            && isset($trade['addtime'])
            && $trade['trade_status'] < self::TRADE_STATUS_AUDIT                // 状态低于请求审核
            && $trade['addtime'] - $now > self::TIME_OUT_PAY                    // 超时
        ) {
            $broker = self::buildTradeBroker($op_user, self::BROKER_TYPE_QY_USER, '');
            $this->executeTrade($broker, $trade, self::TRADE_STATUS_EXPIRE);
        }

        $pay_str = isset($trade['pay_string']) ? $trade['pay_string'] : '';
        if ($trade['trade_status'] < self::TRADE_STATUS_AUDIT) {
            if ($trade['pay_method'] == self::PAYMENT_ALIPAY && empty($pay_str)) {
                $pay_str = $this->getMobilePayStr4Alipay($trade);
            }
        }
        $trade['pay_string'] = $pay_str;
        return $trade;
    }


    public function buildBasicTradeData(
        $op_user, $affect_user, $amount, $pay_method, $channel,
        $receipt, $receipt_type, $one_word, $pal_user)
    {
        $post_id = ($receipt_type == self::RECEIPT_TYP_POST) ? $receipt : 0;
        if ($receipt_type == self::RECEIPT_TYP_COMMENT) {
            ci_load_once('commentm');
            $cmmnt_info = $this->commentm->get_comment_base_info($receipt);
            $post_id = $cmmnt_info['post_id'];
        }

        $trade_data = array(
            "op_user" => $op_user,
            "affect_user" => $affect_user,
            "amount" => $amount,
            "pay_method" => $pay_method,
            "channel" => $channel,
            "receipt" => $receipt,
            "receipt_type" => $receipt_type,
            "one_word" => $one_word,
            "post_id" => $post_id,
            "pal_user" => $pal_user,
        );

        return $trade_data;
    }


    public function getTradeChannel($pay_method, $receipty_type)
    {
        return isset(self::$trade_channel_relation[$pay_method][$receipty_type]) ?
            self::$trade_channel_relation[$pay_method][$receipty_type] :
            self::TRADE_CHANNEL_UNDEFINED;
    }


    public function hashBasicTradeData($basic_trade_data)
    {
        ksort($basic_trade_data);

        $stringToBeHashed = "";
        $i = 0;
        foreach ($basic_trade_data as $k => $v) {
            if (false === empty($v) && "@" != substr($v, 0, 1)) {
                if ($i == 0) {
                    $stringToBeHashed .= "$k" . '="' . "$v\"";
                } else {
                    $stringToBeHashed .= '&' . "$k" . '="' . "$v\"";
                }
                $i++;
            }
        }
        unset ($k, $v);

        return hash('sha1', $stringToBeHashed);
    }


    public function validateTradeData($trade_data, $to_status)
    {
        // 支付方式和交易对象决定 channel, 应该优先校验
        if (empty($trade_data['pay_method'])) {
            return $this->genTradeErrorMSG('校验失败: 未知的支付方式');
        }
        if (empty($trade_data['receipt_type'])) {
            return $this->genTradeErrorMSG('校验失败: 未知的交易对象');
        }
        if (empty($trade_data['receipt'])) {
            return $this->genTradeErrorMSG('校验失败: 交易对象不能为空');
        }

        if ($trade_data['trade_status'] == self::TRADE_STATUS_EXPIRE) {
            return $this->genTradeErrorMSG('校验失败: 当前交易已过期');
        }
        elseif ($trade_data['trade_status'] == self::TRADE_STATUS_SUCCESS) {
            return $this->genTradeErrorMSG('校验失败: 当前交易已经完成');
        }

        // 交易类型
        $chnnl = $trade_data['channel'];
        if ($chnnl == self::TRADE_CHANNEL_UNDEFINED) {
            return $this->genTradeErrorMSG('校验失败: 未知的交易类型');
        }
        elseif (! isset(self::$trade_channel_desc[$chnnl])) {
            return $this->genTradeErrorMSG('校验失败: 暂不支持对此类型交易');
        }

        return false;
    }


    // 是否需要可用的接收方, false:不需要|true:需要
    protected function needValidReceiver($trade)
    {
        if (($trade['receipt_type'] == self::RECEIPT_TYP_RED_PACKET
                && $trade['pay_method'] != self::PAYMENT_UNFOLD_RED_PACKET)
            || ($trade['receipt_type'] == self::RECEIPT_TYP_PART)
        ) {
            return false;
        }
        return true;
    }

    /**
     * 基础交易信息检查
     * @param $broker
     * @param $trade_data
     * @param int $to_status
     * @return array|null
     */
    protected function _validateTrade($broker, $trade_data, $to_status)
    {
        // 需要交易密码的场景
        if (($trade_data['pay_method'] == self::PAYMENT_WALLET
                && $to_status == self::TRADE_STATUS_SUCCESS
                && $broker['broker_type'] == self::BROKER_TYPE_QY_USER)   // 用户钱包发起交易
            || $to_status == self::TRADE_STATUS_AUDIT   // 申请提现
        ) {
            if (empty($broker['passwd'])) {
                return $this->genTradeErrorMSG('校验失败: 交易密码不能为空');
            }

            $op_account = $this->getUserAccount($broker['identity']);
            if (! isset($op_account['wid'])) {
                return $this->genTradeErrorMSG('校验失败: 交易发起方不能为空');
            }

            ci_load_once('userm');
            $res = $this->userm->checkUserWalletPasswd($broker['identity'], $broker['passwd'], $op_account['pay_passwd']);
            if ($res) {
                return $res;
            }

            // 使用钱包支付或者提现, 需要校验余额
            if ($trade_data['amount'] > $op_account['balance']) {
                return $this->genTradeErrorMSG('钱包余额不足');
            }
        }

        if ($to_status == self::TRADE_STATUS_READY) {
            if ($this->needValidReceiver($trade_data)) {
                $affect_account = isset($op_account) ? $op_account : null;
                if ($trade_data['affect_user'] != $trade_data['op_user'] || empty($affect_account)) {
                    $affect_account = $this->getUserAccount($trade_data['affect_user']);
                }
                if (!isset($affect_account['wid'])) {
                    return $this->genTradeErrorMSG('校验失败: 交易接收方不能为空');
                }
            }

            if ($trade_data['pay_method'] == self::PAYMENT_PAL_PAYMENT) {
                $pal_account = $this->getUserAccount($trade_data['pal_user']);

                if (!isset($pal_account['wid'])) {
                    return $this->genTradeErrorMSG('校验失败: 申请交易的朋友不能为空');
                }

                if (! $this->getTradePalRelation($trade_data['pal_user'], $trade_data['op_user'])) {
                    return $this->genTradeErrorMSG('校验失败: 您不是对方的密友');
                }

                if ($trade_data['amount'] > $pal_account['balance']) {
                    return $this->genTradeErrorMSG('校验失败: 钱包余额不足');   // 暂时仅支持用好友钱包支付
                }
            }

            if (empty($trade_data['amount'])) {
                return $this->genTradeErrorMSG('校验失败: 交易金额不能为零');
            }
        }

        if (! isset(self::$trade_status_desc[$to_status])) {
            return $this->genTradeErrorMSG('校验失败: 交易状态有误');
        }

        $res = $this->validateTradeData($trade_data, $to_status);
        if ($res) {
            return $res;
        }

        return null;
    }


    /**
     * @param int $op_user
     * @param int $affect_user
     * @param int $amount 单位:分
     * @param int $pay_method
     * @param string $receipt 用户账户, 帖子id, 评论id
     * @param int $receipt_type
     * @param string $one_word
     * @param int $pal_user
     * @return array|string
     */
    public function initTrade(
        $op_user,
        $affect_user,
        $amount,
        $pay_method,
        $receipt,
        $receipt_type,
        $one_word,
        $pal_user
    ) {
        $op_account = $this->getUserAccount($op_user);
        if (! isset($op_account['wid'])) {
            // create $op_account account
            $op_account = $this->generateUserAccount($op_user);
        }
        if ($op_user == $affect_user) {
            $affect_acount = $op_account;
        }
        else {
            $affect_acount = $this->getUserAccount($affect_user);
            if (! isset($affect_acount['wid']) && $affect_user) {
                // create affect account
                $affect_acount = $this->generateUserAccount($affect_user);
            }
        }

        // 生成交易 ID 之前, 先校验支付密码是否已经设置
        if ($pay_method == self::PAYMENT_WALLET) {
            if (! isset($op_account['pay_passwd']) || empty($op_account['pay_passwd'])) {
                return $this->genTradeErrorMSG('请先设置交易密码', -2);
            }
        }

        ci_load_once('mc', 'cache');
        $channel = $this->getTradeChannel($pay_method, $receipt_type);
        $basic_trade_data = $this->buildBasicTradeData(
            $op_user, $affect_user, $amount, $pay_method, $channel,
            $receipt, $receipt_type, $one_word, $pal_user);

        $hash_key = $this->hashBasicTradeData($basic_trade_data);
        $trade_id = $this->mc->get(self::CACHE_PRE_BASIC_DATA . $hash_key);
        if ($trade_id) {
            $trade = $this->getTradeByTradeID($trade_id, $op_user);
            if ($trade['trade_status'] < self::TRADE_STATUS_AUDIT) {
                // 未进入支付确认状态, 可以发起支付
                return $trade_id;
            }
            else {
                $this->mc->delete(self::CACHE_PRE_BASIC_DATA . $hash_key);
            }
        }

        $broker = self::buildTradeBroker($op_user, self::BROKER_TYPE_QY_USER, '');

        $trade_id = $this->genTradeID();
        $trade = array_merge($basic_trade_data, array('trade_id' => $trade_id, 'trade_status' => self::TRADE_STATUS_INIT));
        $error = $this->executeTrade($broker, $trade, self::TRADE_STATUS_READY);
        if ($error) {
            return $error;
        }

        $this->mc->save(self::CACHE_PRE_BASIC_DATA . $hash_key, $trade_id, self::TIME_OUT_PAY);

        return $trade_id;
    }


    // 交易状态变更唯一入口
    public function executeTrade(
        $broker,
        &$trade,
        $to_status,
        $update_data = null
    )
    {
        $cur_status = $trade['trade_status'];
        $logstr = 'executeTrade trade_id:' . $trade['trade_id'] . '|qbtid:' . $trade['qbtid'] . '|broker:' . $broker['identity'] . '|broker_tpe:' . $broker['broker_type']  . '|from:' . $trade['trade_status'] .'|to:' . $to_status . '|';
        w_log($logstr . '_1_init|update_data:' . json_encode($update_data));

        // 不必执行交易: 无交易状态变更(非初始时), 或已经是成功状态, 或已经是过期状态
        if (($cur_status == $to_status && $to_status != self::TRADE_STATUS_READY)
            || $cur_status == self::TRADE_STATUS_SUCCESS
        ) {
            return false;
        }

        if ($cur_status == self::TRADE_STATUS_EXPIRE) {
            return $this->genTradeErrorMSG('当前交易已过期, 请重新发起支付');
        }

        // 交易前校验
        $error = $this->_validateTrade($broker, $trade, $to_status);
        if ($error) {
            w_log($logstr . '_2__validateTrade|validate_error:' . json_encode($error));
            return $error;
        }

        $res = $this->_coreTradeLogicBlock($broker, $trade, $to_status, $update_data);
        if ($res) {
            w_log($logstr . '_3__coreTradeLogicBlock|sql_debug:'.json_encode(self::$trans_errors));
            return $res;
        }

        $trade['trade_status'] = $to_status;
        $trade['utime'] = time();
        $this->_updateTradeCache($trade);
        w_log($logstr . '_4__updateTradeCache');

        $this->_onTradeStatusChangeNotify($trade, $cur_status, $to_status);
        w_log($logstr . '_5__onTradeStatusChangeNotify');

        return false;
    }


    // 核心操作, 执行交易, 数据库操作
    protected function _coreTradeLogicBlock($broker, &$trade, $to_status, $update_data)
    {
        $logstr = '_coreTradeLogicBlock trade_id:' . $trade['trade_id'] . '|broker:' . $broker['identity'] . '|broker_tpe:' . $broker['broker_type']  . '|from:' . $trade['trade_status'] .'|to:' . $to_status . '|chnnl:' . $trade['channel'] . '|';
        $chnnl = $trade['channel'];
        if (! $this->db->trans_status()) {
            return $this->genTradeErrorMSG('尝试交易状态内部处理失败, 交易状态没有变化');
        }

        $this->db->trans_begin();
        $this->need_rollback = false;

        $old_op_account = $op_account = $this->getUserAccount($trade['op_user']);
        if ($trade['op_user'] == $trade['affect_user']) {
            $affect_account =& $op_account;
            $old_affect_account = $affect_account;
        }
        else {
            $old_affect_account = $affect_account = $this->getUserAccount($trade['affect_user']);
        }
        $pal_account = $this->getUserAccount($trade['pal_user']);
        if ($trade['pal_user'] == $trade['op_user']) {
            $pal_account =& $op_account;
        }
        elseif ($trade['pal_user'] == $trade['affect_user']) {
            $pal_account =& $affect_account;
        }
        $old_pal_account = $pal_account;

        $trans_money = -1 * $trade['amount'];

        $rpinfo = array();
        if ($chnnl == self::TRADE_CHANNEL_AWARD_RED_PACKET_RECEIVE) {
            // 抢红包需要特殊处理
            ci_load_once('walletm');
            $rpinfo = $this->walletm->getRedPacketInfoByID($trade['receipt']);

//            $trade['amount'] = $this->walletm->allotRandomRedPacketAmount($rpinfo);
//            $trans_money = -1 * $trade['amount'];

            if ($rpinfo['count_left'] == 0) {
                $this->need_rollback = true;
                w_log($logstr . '_0_1_RECEIVE_RP|no count left|receiver:' . $trade['affect_user'] );
            }
            elseif ($rpinfo['count_left'] == 1 && $rpinfo['rptype'] == Walletm::RP_TYPE_GROUP_LUCK) {
                if ($rpinfo['amount_left'] != $trade['amount']) {
                    $this->need_rollback = true;
                    w_log($logstr . '_0_2_RECEIVE_RP|last one count error|receiver:' . $trade['affect_user'] . '|amount:' . $trade['amount']);
                }
            }
            else {
                $whe = array(
                    'receipt' => $trade['receipt'],
                    'receipt_type' => self::RECEIPT_TYP_RED_PACKET,
                    'channel' => self::TRADE_CHANNEL_AWARD_RED_PACKET_RECEIVE,
                    'trade_status' => self::TRADE_STATUS_SUCCESS,
                );
                $query = $this->db->select_sum('1', 'n')->where($whe)->get(self::TBL_TRADE);
                if ($query) {
                    $tmp = $query->row_array();
                    if (isset($tmp['n']) && $tmp['n'] >= $rpinfo['count_total']) {
                        $this->need_rollback = true;
                        w_log($logstr . '_0_3_RECEIVE_RP|rp count more than total|receiver:' . $trade['affect_user']);
                    }
                }
            }
        }

        // 关键状态数据库触发操作
        if ($to_status == self::TRADE_STATUS_READY) {
            $this->_saveTrade2DB($trade);
            w_log($logstr . '_1__saveTrade2DB|TRADE_STATUS_READY');
        }
        elseif ($to_status == self::TRADE_STATUS_PRE_SUCCESS) {
            // 用户前台同步调用
            if ($trade['pay_method'] >= self::PAYMENT_ALIPAY
                && $trade['trade_status'] == self::TRADE_STATUS_READY   // 保证仅调用一次
            ) {
                // 第三方支付 用户前台调用 如果未处理过, 先充值, 再扣钱包。
                if ($trade['receipt_type'] == self::RECEIPT_TYP_RED_PACKET) {
                    // 目前仅适配发红包
                    // 发起方充值
                    $this->_addUserAccountHistory($op_account, $trade['amount'], $trade['trade_id'], self::TRADE_CHANNEL_TOP_UP_ALIPAY);
                    w_log($logstr . '_2.1.1__addUserAccountHistory|add money:' . $trade['op_user'] . '|amount:' . $trade['amount']);

                    // 发起方扣款
                    $this->_addUserAccountHistory($op_account, $trans_money, $trade['trade_id'], $chnnl);
                    w_log($logstr . '_2.1.2__addUserAccountHistory|remove money|user:' . $trade['op_user'] . '|amount:' . $trans_money);

                    if ($trade['receipt_type'] == self::RECEIPT_TYP_RED_PACKET) {
                        // 最后发红包
                        $this->_releaseRedPacket($trade['receipt']);
                        w_log($logstr . '_2.1.3__releaseRedPacket|release red packet|user:' . $trade['op_user'] . '|RedPacket:' . $trade['receipt']);
                    }
                }
            }
        }
        elseif ($to_status == self::TRADE_STATUS_SUCCESS) {
            // 所有交易, 最终必须变为成功状态
            if ($chnnl >= self::TRADE_CHANNEL_AWARD_POST_WALLET && $chnnl < self::TRADE_CHANNEL_TOP_UP_ALIPAY) {
                // 消费行为

                if ($chnnl == self::TRADE_CHANNEL_REFUND_RED_PACKET_WALLET) {
                    // 红包退款钱包, 特殊处理
                    $this->_addUserAccountHistory($affect_account, $trade['amount'], $trade['trade_id'], $chnnl);
                    w_log($logstr . '_3.1.1__addUserAccountHistory|add money|user:' . $trade['affect_user'] . '|amount:' . $trade['amount']);

                    $this->_setRedPocketBeRefunded($trade['receipt']);
                    w_log($logstr . '_3.1.2__setRedPocketBeRefunded|rpid:' . $trade['receipt'] . '|count_left:' . $rpinfo['count_left'] . '|amount_left:' . $rpinfo['amount_left']);
                }
                elseif ($chnnl == self::TRADE_CHANNEL_AWARD_RED_PACKET_RECEIVE) {
                    // 抢红包, 特殊处理
                    $this->_addUserAccountHistory($affect_account, $trade['amount'], $trade['trade_id'], $chnnl);
                    w_log($logstr . '_3.2.1__addUserAccountHistory|add money|user:' . $trade['affect_user'] . '|amount:' . $trade['amount']);

                    ci_load_once('mc', 'cache');
                    $key = self::CACHE_RP_RECEIVER . $trade['receipt'] . '_' . $trade['affect_user'];
                    $cch = $this->mc->get($key);
                    if ($cch) {
                        $this->need_rollback = true;
                        w_log($logstr . '_3.2.2_RECEIVE_RP|already receive|receiver:' . $trade['affect_user']);
                    }
                    else {
                        $receivers = $this->getRedPacketReceivers($trade['receipt']);
                        if (isset($receivers[$trade['affect_user']])) {
                            $this->need_rollback = true;
                            w_log($logstr . '_3.2.3_RECEIVE_RP|already be receiver|receiver:' . $trade['affect_user']);
                        }
                    }

                    if ($this->db->trans_status() === FALSE || $this->need_rollback) {
                        //
                    }
                    else {
                        $this->mc->save($key, $trade['trade_id'], self::TIME_OUT_PAY);
                    }

                    $this->_updateRedPocketLeft($trade['receipt'], $trans_money);
                    w_log($logstr . '_3.2.2__updateRedPocketLeft|rpid:' . $trade['receipt'] . '|count_left:' . $rpinfo['count_left'] . '|amount_left:' . $rpinfo['amount_left']);
                }
                elseif ($chnnl >= self::TRADE_CHANNEL_AWARD_LIMIT_BY_WALLET) {
                    // 第三方支付回调
                    if ($trade['receipt_type'] == self::RECEIPT_TYP_RED_PACKET
                        && $trade['trade_status'] != self::TRADE_STATUS_READY     // 保证仅调用一次
                    ) {
                        // pass
                    }
                    else {
                        // 发起方充值
                        $this->_addUserAccountHistory($op_account, $trade['amount'], $trade['trade_id'], self::TRADE_CHANNEL_TOP_UP_ALIPAY);
                        w_log($logstr . '_3.3.1__addUserAccountHistory|add money:' . $trade['op_user'] . '|amount:' . $trade['amount']);

                        // 发起方扣款
                        $this->_addUserAccountHistory($op_account, $trans_money, $trade['trade_id'], $chnnl);
                        w_log($logstr . '_3.3.2__addUserAccountHistory|remove money|user:' . $trade['op_user'] . '|amount:' . $trans_money);

                        if ($this->needValidReceiver($trade) && $affect_account) {
                            // 收款方收款
                            $this->_addUserAccountHistory($affect_account, $trade['amount'], $trade['trade_id'], $chnnl);
                            w_log($logstr . '_3.3.3__addUserAccountHistory|add money|user:' . $trade['affect_user'] . '|amount:' . $trade['amount']);
                        }
                    }
                }
                else {
                    // 默认行为, 发起方扣款, 如果需要收款方, 收款方收款
                    // 发起方扣款
                    $this->_addUserAccountHistory($op_account, $trans_money, $trade['trade_id'], $chnnl);
                    w_log($logstr . '_3.4.1__addUserAccountHistory|remove money|user:' . $trade['op_user'] . '|amount:' . $trans_money);

                    if ($this->needValidReceiver($trade) && $affect_account) {
                        // 收款方收款
                        $this->_addUserAccountHistory($affect_account, $trade['amount'], $trade['trade_id'], $chnnl);
                        w_log($logstr . '_3.4.2__addUserAccountHistory|add money|user:' . $trade['affect_user'] . '|amount:' . $trade['amount']);
                    }
                }

                if ($trade['pay_method'] == self::PAYMENT_WALLET
                    || $trade['pay_method'] == self::PAYMENT_PAL_PAYMENT
                    || $trade['pay_method'] >= self::PAYMENT_ALIPAY
                ) {
                    // 支付方式是钱包, 好友支付, 或者支付宝。有实际付款方。不是抢红包或者退款。
                    if ($trade['receipt_type'] == self::RECEIPT_TYP_POST) {
                        $this->_updatePostRewardAmount($trade['receipt'], $trade['amount']);
                        w_log($logstr . '_3.5.1__updatePostRewardAmount|add money|post:' . $trade['receipt'] . '|amount:' . $trans_money);
                    }
                    elseif ($trade['receipt_type'] == self::RECEIPT_TYP_COMMENT) {
                        $this->_updateCommentRewardAmount($trade['receipt'], $trade['amount']);
                        w_log($logstr . '_3.5.2__updateCommentRewardAmount|add money|comment:' . $trade['receipt'] . '|amount:' . $trade['amount']);
                    }
                    elseif ($trade['receipt_type'] == self::RECEIPT_TYP_RED_PACKET) {
                        if ($trade['pay_method'] >= self::PAYMENT_ALIPAY
                            && $trade['trade_status'] != self::TRADE_STATUS_READY     // 第三方支付回调 保证仅调用一次
                        ) {
                            // pass
                        }
                        else {
                            $this->_releaseRedPacket($trade['receipt']);
                            w_log($logstr . '_3.5.3__releaseRedPacket|release red packet|user:' . $trade['op_user'] . '|rpid:' . $trade['receipt']);
                        }
                    }
                }
            }
            elseif ($chnnl >= self::TRADE_CHANNEL_TOP_UP_ALIPAY && $chnnl < self::TRADE_CHANNEL_WITH_DRAW_LIMIT_FROM_WALLET) {
                // 充值, 被充值方加钱
                $this->_addUserAccountHistory($affect_account, $trade['amount'], $trade['trade_id'], $chnnl);
                w_log($logstr . '_3.6.1__addUserAccountHistory|add money|user:' . $trade['affect_user'] . '|amount:' . $trade['amount']);
            }
            elseif ($chnnl >= self::TRADE_CHANNEL_WITH_DRAW_LIMIT_FROM_WALLET) {
                // 提现, 钱包扣款已在审核通过那里已经执行过
                // pass
            }
        }
        elseif ($to_status == self::TRADE_STATUS_AUDIT) {
            if ($chnnl >= self::TRADE_CHANNEL_WITH_DRAW_LIMIT_FROM_WALLET) {
                $this->_saveBGWithdrawAudit($trade, $update_data);
                // 提现, 交易支付密码无误, 即可扣款
                $this->_addUserAccountHistory($op_account, $trans_money, $trade['trade_id'], $chnnl);
                w_log($logstr . '_4__addUserAccountHistory|remove money|user:' . $trade['affect_user'] . '|amount:' . $trans_money);
            }
        }
        elseif ($to_status == self::TRADE_STATUS_AUDIT_FAILED) {
            if ($chnnl >= self::TRADE_CHANNEL_WITH_DRAW_LIMIT_FROM_WALLET) {
                // 提现, 审核失败, 扣款打回
                $this->_addUserAccountHistory($op_account, $trade['amount'], $trade['trade_id'], $chnnl);
                w_log($logstr . '_5__addUserAccountHistory|add money|user:' . $trade['affect_user'] . '|amount:' . $trade['amount']);
            }
        }
        elseif ($to_status == self::TRADE_STATUS_PAL_AUDIT_SUCCEED) {
            // 帮好友代付, 给好友钱包打钱
            $this->_addUserAccountHistory($pal_account, $trans_money, $trade['trade_id'], $chnnl);
            w_log($logstr . '_6.1__addUserAccountHistory|remove money|user:' . $trade['pal_user'] . '|amount:' . $trans_money);
            $this->_addUserAccountHistory($op_account, $trade['amount'], $trade['trade_id'], $chnnl);
            w_log($logstr . '_6.2__addUserAccountHistory|add money|user:' . $trade['op_user'] . '|amount:' . $trade['amount']);
        }

        // 真正更新用户余额
        if ($old_op_account['balance'] != $op_account['balance']) {
            $this->_updateUserBalance($op_account);
            w_log($logstr . '_7__updateUserBalance|user:' . $op_account['uid'] . '|amount:' . $trade['amount'] . '|balance:' . $op_account['balance']);
        }
        if ($trade['op_user'] != $trade['affect_user'] && $old_affect_account['balance'] != $affect_account['balance']) {
            $this->_updateUserBalance($affect_account);
            w_log($logstr . '_8__updateUserBalance|user:' . $affect_account['uid'] . '|amount:' . $trade['amount'] . '|balance:' . $affect_account['balance']);
        }
        if (isset($pal_account['balance'])
            && $trade['pal_user'] != $trade['op_user']
            && $trade['pal_user'] != $trade['affect_user']
            && $old_pal_account['balance'] != $pal_account['balance']
        ) {
            $this->_updateUserBalance($pal_account);
            w_log($logstr . '_9__updateUserBalance|user:' . $pal_account['uid'] . '|amount:' . $trade['amount'] . '|balance:' . $pal_account['balance']);
        }


        // 交易状态操作, 每个订单每个状态只能有一个
        $this->_updateTradeData($trade, $to_status, $update_data);
        // 数据库操作失败、数据回滚, 返回错误
        if ($this->db->trans_status() === FALSE || $this->need_rollback)
        {
            $this->db->trans_rollback();

//            self::$trans_errors
            w_log($logstr . '_rollback|need_rollback:' . $this->need_rollback);

            return $this->genTradeErrorMSG('尝试交易状态变更失败, 交易状态没有变化');
        }

        $this->db->trans_commit();
        w_log($logstr . '_commit');
        return false;
    }


    protected function _updateTradeData(&$trade, $trade_status_to, $update_data)
    {
        if ($this->db->trans_status() === FALSE || $this->need_rollback) {
            return ;
        }
        // 交易变更描述
        $stage_desc = isset(self::$trade_status_desc[$trade_status_to])
            ? self::$trade_status_desc[$trade_status_to]
            : "交易状态变更";


        // 唯一性校验
        // CONSTRAINT uc_qb_status UNIQUE (trade_id,status_after)
        $detail_data = array(
            "trade_id" => $trade['trade_id'],
            "uid" => $trade['op_user'],
            "stage_desc" => $stage_desc,
            "addtime" => time(),
            "status_before" => $trade['trade_status'],
            "status_after" => $trade_status_to
        );
        if (! empty($update_data)) {
            $detail_data['upt_data'] = json_encode($update_data);
        }

        // '生成交易详情失败'
        $this->db->insert(self::TBL_TRADE_DETAIL, $detail_data);
        self::_fetchSQLError($this->db);

        $data_upt = array(
            "trade_status" => $trade_status_to,
            "utime" => time(),
        );
        if (isset($update_data[self::FIELD_AUDIT_ID])) {
            unset($update_data[self::FIELD_AUDIT_ID]);
        }
        if (! empty($update_data)) {
            $data_upt = array_merge($update_data, $data_upt);
        }
        // '更新交易状态失败'
        $this->db->update(self::TBL_TRADE, $data_upt, array('qbtid' => $trade['qbtid']));
        self::_fetchSQLError($this->db);

        $trade = array_merge($trade, $data_upt);
    }


    /**
     * @see https://tower.im/projects/c03bde9c760f4c0eaaf24a3586726e0e/docs/cfd06fadc73b412fbe98498e8b8ebcab/#31b7682340314a269c1489f777a34131
     * @param $trade
     * @param $cur_status
     * @param $to_status
     */
    protected function _onTradeStatusChangeNotify($trade, $cur_status, $to_status)
    {
        $logstr = '_onTradeStatusChangeNotify trade_id:' . $trade['trade_id'] . '|';
        if ($to_status == self::TRADE_STATUS_AUDIT) {
            $message = '您的提现申请已提交，轻盈客服将于3—5天内完成审核并转账，请留意消息提醒并及时查看账户金额变动';
            ci_load_once('messagem');
            $send_content = json_encode(array("content"=>$message));
            $this->messagem->send_msg_basic(31495, $trade['affect_user'], $send_content, $apptype=Messagem::MSG_APP_TYPE_PERSONAL, $msgtype=3);
            w_log($logstr . '_1.1_TRADE_STATUS_AUDIT|message|uid:' . $trade['affect_user'] .'|msg:' . $send_content);
        }
        if ($to_status == self::TRADE_STATUS_AUDIT_FAILED) {
            // 申请提现失败
            $push_msg = '您的提现申请未通过审核。如有疑问，请联系轻盈客服查看详细信息。';
            ci_load_once('pushm');
            $this->pushm->sendByUserID($trade['affect_user'], $push_msg, array());
            w_log($logstr . '_2.1_TRADE_STATUS_AUDIT_FAILED|push|uid:' . $trade['affect_user'] .'|msg:' . $push_msg);

            $affect_account = $this->getUserAccount($trade['affect_user']);
            $balance = $this->convertFen2Yuan($affect_account['balance']);
            $sms_msg = sprintf('尊敬的用户，您的本次提现失败，当前轻盈账户余额%.2f元。请登录轻盈账号查看详细信息。', $balance);
            ci_load_once('utilm');
            $userinfo_op = $this->userm->getUserInfoById($trade['affect_user']);
            $this->utilm->sendSMS($userinfo_op['cellphone'], $sms_msg);
            w_log($logstr . '_2.2_TRADE_STATUS_AUDIT_FAILED|sms|uid:' . $trade['affect_user'] .'|msg:' . $sms_msg . '|cellphone:'. $userinfo_op['cellphone']);

            $message = '您的提现申请未通过审核。如有疑问，请联系轻盈客服查看详细信息。';
            ci_load_once('messagem');
            $send_content = json_encode(array("content"=>$message));
            $this->messagem->send_msg_basic(31495, $trade['affect_user'], $send_content, $apptype=Messagem::MSG_APP_TYPE_PERSONAL, $msgtype=3);
            w_log($logstr . '_2.3_TRADE_STATUS_AUDIT_FAILED|message|uid:' . $trade['affect_user'] .'|msg:' . $send_content);
        }
        if ($to_status == self::TRADE_STATUS_AUDIT_SUCCEED) {
            // 申请提现失败
            $push_msg = '您的提现金额已到账，请注意查收确认。';
            ci_load_once('pushm');
            $this->pushm->sendByUserID($trade['affect_user'], $push_msg, array());
            w_log($logstr . '_3.1_TRADE_STATUS_AUDIT_FAILED|push|uid:' . $trade['affect_user'] .'|msg:' . $push_msg);

            $amount = $this->convertFen2Yuan($trade['amount']);
            $affect_account = $this->getUserAccount($trade['affect_user']);
            $balance = $this->convertFen2Yuan($affect_account['balance']);
            $sms_msg = sprintf('尊敬的用户，您的本次提现已成功，共提现%.2f元，轻盈账户余额%.2f元，请注意查收确认。', $amount, $balance);
            // send sms
            ci_load_once('utilm');
            ci_load_once('userm');
            $userinfo_op = $this->userm->getUserInfoById($trade['affect_user']);
            $this->utilm->sendSMS($userinfo_op['cellphone'], $sms_msg);
            w_log($logstr . '_3.2_TRADE_STATUS_AUDIT_SUCCEED|sms|uid:' . $trade['affect_user'] .'|msg:' . $sms_msg . '|cellphone:'. $userinfo_op['cellphone']);

            $message = '您的提现金额已到账，请注意查收确认。';
            ci_load_once('messagem');
            $send_content = json_encode(array("content"=>$message));
            $this->messagem->send_msg_basic(31495, $trade['affect_user'], $send_content, $apptype=Messagem::MSG_APP_TYPE_PERSONAL, $msgtype=3);
            w_log($logstr . '_3.3_TRADE_STATUS_AUDIT_SUCCEED|message|uid:' . $trade['affect_user'] .'|msg:' . $send_content);
        }

        if ($to_status == self::TRADE_STATUS_SUCCESS
            && ($trade['receipt_type'] == self::RECEIPT_TYP_PART
            )
        ) {
            ci_load_once('packagem');
            {
                w_log('图谱支付成功回调');
                w_log(json_encode(array($trade['op_user'],$trade['receipt'],'5',$this->unique_code,$trade['pay_method'],$trade['amount'])));
                $this->packagem->insert_part_order($trade['op_user'],$trade['receipt'],'5',$this->unique_code,$trade['pay_method'],$trade['amount'],$trade['trade_id']);
            }
        }

        if ($to_status == self::TRADE_STATUS_SUCCESS
            && ($trade['receipt_type'] == self::RECEIPT_TYP_POST
                || $trade['receipt_type'] == self::RECEIPT_TYP_COMMENT
            )
        ) {
            $receipt_desc = '发帖';
            $post_id = $trade['receipt'];
            $comment_id = 0;
            if ($trade['receipt_type'] == self::RECEIPT_TYP_COMMENT) {
                $receipt_desc = '评论';
                ci_load_once('commentm');
                $cmmt_info = $this->commentm->get_comment_base_info($trade['receipt']);
                $post_id = $cmmt_info['post_id'];
                $comment_id = $trade['receipt'];
            }

            ci_load_once('pushm');
            $amount = $this->convertFen2Yuan($trade['amount']);
            $short_url = $this->genSMSShortUrl($trade['affect_user'], $post_id, $comment_id);
            $detail_msg = '';
            if (! empty($short_url)) {
                $detail_msg = sprintf('，可点此处查看%s内容 %s', $receipt_desc, $short_url);
            }
            $sms_msg = sprintf(
                '尊敬的用户，您的%s非常优秀，有人给您打赏了%.2f元%s。具体钱包详情请登录最新版本轻盈医学APP内查看，感谢您对我们的支持与关注！',
                $receipt_desc, $amount, $detail_msg);
            // send sms
            ci_load_once('utilm');
            ci_load_once('userm');
            $userinfo_affect = $this->userm->getUserInfoById($trade['affect_user']);
            $this->utilm->sendSMS($userinfo_affect['cellphone'], $sms_msg);
            w_log($logstr . '_4.1_TRADE_STATUS_SUCCESS|sms|uid:' . $trade['affect_user'] .'|msg:' . $sms_msg . '|cellphone:'. $userinfo_affect['cellphone']);

            ci_load_once('messagem');
            ci_load_once('userm');
            $userinfo_op = $this->userm->getUserInfoById($trade['op_user']);
            $amount = $this->convertFen2Yuan($trade['amount']);
            $send_content = sprintf('[user:%s:%s]认为您的%s内容优质，打赏了%.2f元。', $userinfo_op['id'], $userinfo_op['username'], $receipt_desc, $amount);
            if (!empty($trade['one_word'])) {
                $send_content .= '打赏留言：' . $trade['one_word'];
            }
            $this->messagem->send_message($post_id, $trade['affect_user'], $send_content, 31495);
            w_log($logstr . '_4.2_TRADE_STATUS_SUCCESS|message|uid:' . $trade['affect_user'] .'|msg:' . $send_content);

            $push_msg = sprintf('您的%s非常优秀，已被打赏%.2f元，', $receipt_desc, $amount);
            if (version_compare($userinfo_affect['softversion'], '2.4') < 0) {
                // 低版本
                $push_msg .= '快到最新版本轻盈医学查看吧。';
            }
            else {
                $push_msg .= '快来查看吧。';
            }
            ci_load_once('pushm');
            $this->pushm->sendByUserID($trade['affect_user'], $push_msg, array());
            w_log($logstr . '_4.2.1_TRADE_STATUS_SUCCESS|push|uid:' . $trade['affect_user'] .'|msg:' . $push_msg);

            // 足额打赏—短信
            $affect_account = $this->getUserAccount($trade['affect_user']);
            // 接收打赏, 或抢红包。余额上升后正好跨越提示额度
            if (($trade['receipt_type'] == self::RECEIPT_TYP_POST
                    || $trade['receipt_type'] == self::RECEIPT_TYP_COMMENT
                    || $trade['channel'] == self::TRADE_CHANNEL_AWARD_RED_PACKET_RECEIVE)
                && (($affect_account['balance'] < self::BALANCE_NOTIFY_LEVEL_ONE + $trade['amount']
                        && $affect_account['balance'] >= self::BALANCE_NOTIFY_LEVEL_ONE)
                    || ($affect_account['balance'] < self::BALANCE_NOTIFY_LEVEL_TWO + $trade['amount']
                        && $affect_account['balance'] >= self::BALANCE_NOTIFY_LEVEL_TWO))
            ) {
                $balance = $this->convertFen2Yuan($affect_account['balance']);
                $sms_msg = sprintf('尊敬的用户，您的轻盈钱包金额已经达到%.2f元，赶快登录轻盈医学查看吧！', $balance);

                ci_load_once('utilm');
                $this->utilm->sendSMS($userinfo_affect['cellphone'], $sms_msg);
                w_log($logstr . '_4.4_TRADE_STATUS_SUCCESS|sms|uid:' . $trade['affect_user'] .'|msg:' . $sms_msg . '|cellphone:'. $userinfo_affect['cellphone']);
            }
        }
        w_log($logstr . '_notify is over');
    }


    public function getMobilePayStr4Alipay($trade)
    {
        $this->load->model('ali_mobilepaym');

        $min_expire = (int)(PAY_ALI_EXPIRE - (time() - (int)$trade["addtime"]) / 60);
        if ($min_expire <= 0) {
            return '';
        }

        $amount = $this->convertFen2Yuan($trade["amount"]);

        $notify_url = $this->getTradeNotifyUrl();

        $title = '轻盈医学-消费';
        if ($trade['channel'] >= self::TRADE_CHANNEL_WITH_DRAW_LIMIT_FROM_WALLET) {
            $title = '轻盈医学-提现';
        }
        elseif ($trade['channel'] == self::TRADE_CHANNEL_AWARD_RED_PACKET_ALIPAY) {
            $title = '轻盈医学-发红包';
        }
        elseif($trade['channel'] == self::TRADE_CHANNEL_AWARD_PART_ALIPAY) {
            $title = '轻盈医学-图谱购买';
        }
        elseif ($trade['channel'] >= self::TRADE_CHANNEL_AWARD_POST_WALLET && $trade['channel'] < self::TRADE_CHANNEL_TOP_UP_ALIPAY) {
            $title = '轻盈医学-打赏';
        }
        elseif ($trade['channel'] >= self::TRADE_CHANNEL_TOP_UP_ALIPAY && $trade['channel'] < self::TRADE_CHANNEL_WITH_DRAW_LIMIT_FROM_WALLET) {
            $title = '轻盈医学-充值';
        }

        return $this->ali_mobilepaym->genQuotePayInfo($trade["trade_id"], $title, $title, $amount, $min_expire, $notify_url);
    }


    public function parseTradeIDFromAliCallBackString($ali_pay_str)
    {
        if (empty($ali_pay_str)) {
            return $this->genTradeErrorMSG('支付宝交易信息为空');
        }

        $this->load->helper('alipay_utils');

        $params = parsetSignedContent($ali_pay_str);

        if (!isset($params['sign'])) {
            return $this->genTradeErrorMSG('支付宝交易信息签名信息缺失');
        }
        if (!isset($params['out_trade_no'])) {
            return $this->genTradeErrorMSG('支付宝交易信息交易信息缺失');
        }

        $sign = $params['sign'];
        $success = $params['success'];

        unset($params['sign']);
        unset($params['sign_type']);
        unset($params['success']);
        ksort($params);

        $pre_str = getQuoteSignContent($params) . "&success=\"{$success}\"";

        if (! rsaVerify($pre_str, PAY_ALI_PUBLIC_KEY_MOBILE, $sign)) {
            return $this->genTradeErrorMSG('支付宝交易信息, 校验失败');
        }

        if ($success != 'true') {
            return $this->genTradeErrorMSG('用户取消支付');
        }

        return $params['out_trade_no'];
    }

    public function thirdFrontendCall($op_user, $signed_str)
    {
        $res = $this->parseTradeIDFromAliCallBackString($signed_str);
        if (is_array($res)) {
            return $res;
        }

        $broker = self::buildTradeBroker($op_user, self::BROKER_TYPE_QY_USER, '');
        $trade = $this->getTradeByTradeID($res, $op_user);
        $res = $this->executeTrade($broker, $trade, Tradem::TRADE_STATUS_PRE_SUCCESS);
        if ($res) {
            return $res;
        }

        return false;
    }
    /**
     *  所有发生交易的渠道, 1-100 产品内消费, 101-10000 使用第三方进入产品消费, 10001-11000 第三方充值, 11001-12000 提现至第三方
     * 处理用户账单
     */
    public function handle_user_wallet($detail)
    {
        ci_load_once('packagem');
        $result = array();

        $uids = [];
        foreach ($detail as $value) {
            if (! isset($uids[$value['op_user']])) {
                $uids[$value['op_user']] = 1;
            }
            if (($value['op_user'] != $value['affect_user'])
                && (! isset($uids[$value['affect_user']]))
            ) {
                $uids[$value['affect_user']] = 1;
            }
        }
        $uids = array_keys($uids);

        ci_load_once('userm');
        $userinfo_map = $this->userm->getUserInfoByIDsForShow($uids);

        $i = 0;
        foreach ($detail as $k => $v) {
            if ($v['amount'] > 0) {
                if ($v['channel'] >= self::TRADE_CHANNEL_AWARD_LIMIT_BY_WALLET
                    && $v['channel'] <= self::TRADE_CHANNEL_TOP_UP_ALIPAY
                    && $v['uid'] == $v['op_user']
                ) {
                    continue;
                }
            }
            $result[$i]['channel'] = self::$trade_channel_desc[$v['channel']];
            $result[$i]['amount'] =  $v['amount']>=0? '+'.sprintf("%.2f",$this->convertFen2Yuan($v['amount'])).'元':sprintf("%.2f",$this->convertFen2Yuan($v['amount'])).'元';
            $result[$i]['addtime'] = date('Y-m-d H:i:s',$v['addtime']);
            $result[$i]['receipt_type'] = $v['receipt_type'];
            $result[$i]['receipt'] = $v['receipt'];

            if ($v['uid'] == $v['op_user']) {
                $userinfo = $userinfo_map[$v['affect_user']];
            }
            else {
                $userinfo = $userinfo_map[$v['op_user']];
            }
            $result[$i]['username'] = $userinfo['username'];
            $result[$i]['user_id'] = $userinfo['id'];

            #大于0  10001-11000 第三方充值
            if ($v['amount'] > 0) {
//                if ($v['channel'] >= self::TRADE_CHANNEL_AWARD_LIMIT_BY_WALLET
//                    && $v['channel'] <= self::TRADE_CHANNEL_TOP_UP_ALIPAY
//                    && $v['uid'] == $v['op_user']
//                ) {
//                    $result[$k]['channel'] = '收入';
//                    $result[$k]['desc'] = "支付宝充值";
//
//                }
//                else {
                    $result[$i]['channel'] = '收入';
                    $result[$i]['desc'] = "收赏来自";
                    if($v['receipt_type']==self::RECEIPT_TYP_POST) {
                        //
                    }
                    elseif ($v['receipt_type']==self::RECEIPT_TYP_COMMENT) {
                        //
                    }
                    elseif ($v['channel']==self::TRADE_CHANNEL_REFUND_RED_PACKET_WALLET) {
                        $result[$i]['desc'] = "红包过期退款";
                    }
                    elseif ($v['receipt_type'] == self::RECEIPT_TYP_RED_PACKET) {
                        $result[$i]['desc'] = "收红包";
                    }
                    else {
                        $result[$i]['desc'] = "未知";
                    }
//                }
            }
            else
            {
                $result[$i]['channel'] = '支出';
                #11001-12000 提现至第三方
                if ($v['channel'] >= self::TRADE_CHANNEL_WITH_DRAW_ALIPAY
                    && $v['channel'] <= self::TRADE_CHANNEL_WITH_DRAW_WEICHAT
                    && $v['receipt_type'] != 3
                ) {
                    $result[$i]['desc'] = '提现到'.$this->get_account_by_qbaid($v['receipt']);
                }
                else {
                    if ($v['receipt_type'] == self::RECEIPT_TYP_POST) {
                        $result[$i]['desc'] = "打赏";
                    }
                    elseif ($v['receipt_type'] == self::RECEIPT_TYP_COMMENT) {
                        $result[$i]['desc'] = "打赏";
                    }
                    elseif ($v['receipt_type'] == self::RECEIPT_TYP_RED_PACKET) {
                        // 收红包
                        $result[$i]['desc'] = "发红包";
                    }
                    elseif ($v['receipt_type'] == self::RECEIPT_TYP_PART) {
                        $one_package_static_info = $this->packagem->get_package_staic_content($v['receipt'],$v['op_user']);
                        $result[$i]['desc'] = $v['cmmnt'];
                        $result[$i]['username'] = $one_package_static_info['atlas_name'];
                        $result[$i]['user_id']  = 0;
                    }
                    else {
                        $result[$i]['desc'] = "未知";
                    }
                }
            }
            $i++;
         }
        return $result;
    }



    /**
     * 查询提现账号
     */
    public function get_account_by_qbaid($qbaid)
    {
        $res = $this->db->select('email')->from('qb_user_relate_account')->where('qbaid',$qbaid)->get()->row_array();
        return $res['email'];
    }


    /**
     * 交易执行者
     * @param string $identity 身份
     * @param int $broker_type 类别
     * @param string $passwd 密码
     * @return array
     */
    static public function buildTradeBroker($identity, $broker_type = self::BROKER_TYPE_QY_USER, $passwd = '')
    {
        $broker = array(
            "broker_type" => $broker_type,
            "identity" => $identity,
            "passwd" => $passwd,
        );
        return $broker;
    } 


    public function getBGAuditRecord($audit_id)
    {
        if (empty($audit_id)) {
            return null;
        }

        $query = $this->db->where('id', $audit_id)->get(self::TBL_BG_WITHDRAW_AUDIT, 1);
        $record = null;
        if ($query) {
            $record = $query->row_array();
        }
        return $record;
    }


    /**
     * 短信通知用, 短链接地址
     * @param $user_id
     * @param $post_id
     * @param int $comment_id
     * @param int $channel
     * @return bool|null
     */
    public function genSMSShortUrl($user_id, $post_id, $comment_id = 0, $channel = 1)
    {
        if (empty($post_id)) {
            return null;
        }
        $query = $this->db->select('short_url')->where(array('uid' => $user_id, 'post_id' => $post_id, "comment_id" => $comment_id))->get(self::TBL_SMS_EXPAND_POST, 1);
        if ($query) {
            $data = $query->row_array();
            if (isset($data['short_url']) && ! empty($data['short_url'])) {
                return $data['short_url'];
            }
        }

        $sc = implode(';;', [$user_id, $post_id, $comment_id, $channel, '']);
        $sc = urlencode(base64_encode($sc));


        $url = WEB_URL . "share/threads/{$post_id}?sc={$sc}";
        ci_load_once('wechatm');
        $short_url = $this->wechatm->getShortUrl($url);
        if (empty($short_url)) {
            return false;
        }

        $data = array(
            "channel" => $channel,
            "uid" => $user_id,
            "post_id" => $post_id,
            "comment_id" => $comment_id,
            "sc" => $sc,
            "short_url" => $short_url,
            "addtime" => time(),
        );
        $this->db->insert(self::TBL_SMS_EXPAND_POST, $data);

        return $short_url;
    }


    public function getTradePalRelation($uid, $trust_user)
    {
        $query = $this->db->select('need_audit')->where(array('uid' => $uid, "trust_user" => $trust_user))->get(self::TBL_USER_TRUST_RELATION, 1);
        if ($query) {
            return $query->row_array();
        }
        return false;
    }

    // 个人定向红包领取用户信息(单人)
    public function get_red_wallet_userInfo($red_wallet_id = 0, $red_wallet_info = array())
    {
        $filters = array('receipt' => $red_wallet_id, 'amount' => $red_wallet_info['amount_total'], 'trade_status' => self::TRADE_STATUS_SUCCESS, 'receipt_type' => self::RECEIPT_TYP_RED_PACKET, 'op_user' => $red_wallet_info['uid']);
        $query = $this->db->select('affect_user')->where($filters)->get(self::TBL_TRADE, 1);
        $data = null;
        if ($query) {
            $data = $query->row_array();
        }
        return $data;
    }

    //  群红包用户信息
    public function get_red_wallet_group_userInfo($red_wallet_id = 0)
    {
        $filters = array('receipt' => $red_wallet_id, 'trade_status' => self::TRADE_STATUS_SUCCESS, 'pay_method' => self::PAYMENT_UNFOLD_RED_PACKET);
        $query = $this->db->select('affect_user,amount,addtime,utime,one_word,trade_id')->from(self::TBL_TRADE)->where($filters)->order_by('addtime', 'desc')->order_by('qbtid', 'desc')->get();
        $data = null;
        if ($query) {
            $data = $query->result_array();
        }
        return $data;
    }


    /**
     * @param int $rpid 红包id
     * @param int $observer 观察者
     * @param int $is_4client 是否给客户端调用
     * @return array
     */
    public function getRedPacketReceivers($rpid, $observer = 0, $is_4client = 0)
    {
        ci_load_once('walletm');
        $rpinfo = $this->walletm->getRedPacketInfoByID($rpid);
        if (empty($rpinfo)) {
            return array();
        }

        $items = $this->get_red_wallet_group_userInfo($rpid);
        if (empty($items)) {
            return array();
        }

        $user_ids = array_column($items, 'affect_user');
        ci_load_once('userm');
        $userinfo_map = $this->userm->getUserInfoByIDsForShow($user_ids, $observer);

        $result = array();
        foreach ($items as $item) {
            $uid = $item['affect_user'];
            if (! isset($userinfo_map[$uid])) {
                continue;
            }
            $data = $userinfo_map[$uid];
            $data = $this->genSingleReceiverInfo($data, $item, $rpinfo['winner'], $is_4client);
            $result[$uid] = $data;
        }

        return $result;
    }


    // 生成单条接收者信息
    public function genSingleReceiverInfo($user_data, $trade, $winner_id, $is_4client)
    {
        $user_data['receive_time'] = $trade['addtime'];
        $user_data['trade_id'] = $trade['trade_id'];
        $user_data['amount'] = $is_4client ? $this->convertFen2Yuan($trade['amount']) : $trade['amount'];
        $user_data['is_winner'] = 0;
        if ($user_data['id'] == $winner_id) {
            $user_data['is_winner'] = 1;
        }
        return $user_data;
    }
}